import fs from 'node:fs'
import { dirname, join } from 'node:path'

import openapiTS from 'openapi-typescript'
import type { PluginOption } from 'vite'

const prefix = `/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck

export type Method =
  | 'get'
  | 'post'
  | 'delete'
  | 'put'
  | 'patch'
  | 'head'
  | 'options'

export type PathWrapper<
  Paths,
  Keys extends keyof Paths = keyof Paths,
  M extends Method = Method
> = {
  [K in Keys]: {
    url: K
    method: keyof Paths[K] & M
    op: Paths[K][keyof Paths[K] & M]
  }
}

export type Path<
  Paths,
  URL extends keyof Paths,
  M extends Method = Method
> = Pick<PathWrapper<Paths, URL, M>, URL>[URL]

export type ApiParameter<
  Paths,
  URL extends keyof Paths,
  M extends Method = Method
> = Path<Paths, URL, M>['op'] extends {
  parameters?: {
    query?: infer Q
    body?: infer B
    path?: infer P
    formData?: infer F
  }
  requestBody?: {
    content: {
      'application/json': infer RB
    }
  }
}
  ? P & Q & (B extends Record<string, unknown> ? B[keyof B] : unknown) & F & RB
  : Record<string, any> | void

export type ApiReturn<
  Paths,
  URL extends keyof Paths,
  M extends Method = Method,
  STATE extends number = 200
> = Path<Paths, URL, M>['op'] extends {
  responses: infer R
}
  ? {
      [RK in keyof R]: R[RK] extends { schema?: infer S }
        ? S
        : R[RK] extends { content: { 'application/json': infer C } }
        ? C
        : RK extends 'default'
        ? R[RK]
        : unknown
    }[keyof R & STATE]
  : never

export type API<
  Paths,
  URL extends keyof Paths,
  M extends Method = Method,
  STATE extends number = 200
> = (
  params: ApiParameter<Paths, URL, M>,
  ...args: any
) => Promise<ApiReturn<Paths, URL, M, STATE>>

export type PromiseFn = (...args: any) => Promise<any>

export type CommonAPI<
  T extends keyof paths,
  M extends Method = Method,
  S extends number = 200
> = API<paths, T, M, S>

export type CommonParams<
  T extends keyof paths,
  M extends Method = Method
> = ApiParameter<paths, T, M>

export type CommonReturn<
  T extends keyof paths,
  M extends Method = Method,
  STATE extends number = 200
> = ApiReturn<paths, T, M, STATE>

export type CommonReturnListItem<
  T extends keyof paths,
  M extends Method = Method,
  K extends string = 'list',
  STATE extends number = 200
> = CommonReturn<T, M, STATE> extends { [Key in K]?: Array<infer R> }
  ? R
  : never
`

interface GenerateApiSchemaOptions {
  // swagger json地址
  conf: {
    base: string
    services: {
      name: string
      url: string
    }[]
  }
  dir?: string
}

/**
 * 根据swagger api json生成ts类型文件
 * @param options
 * @returns
 */
export function generateApiSchema(
  options?: GenerateApiSchemaOptions
): PluginOption {
  const defaultOptions: Omit<GenerateApiSchemaOptions, 'conf'> = {
    dir: 'src/apis/schema/'
  }
  const opt = { ...defaultOptions, ...options }

  if (!opt.conf?.services?.length) {
    console.error('未提供api json文档地址')
    return
  }

  return {
    name: 'generate-api-schema',
    async options() {
      const { base, services } = opt.conf!
      services.forEach(async ({ name, url }) => {
        const rootDir = process.cwd()
        const filePath = join(rootDir, opt.dir!, `${name}.ts`)
        const dirPath = dirname(filePath)
        if (!fs.existsSync(dirPath)) {
          fs.mkdirSync(dirPath, { recursive: true })
        }
        process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0' //禁止证书检查
        const output = await openapiTS(base + url, {
          version: 2,
          defaultNonNullable: true
        }).catch(console.warn)
        if (output && output?.length > 195) {
          fs.writeFileSync(filePath, prefix + output)
          console.log(`生成dts文件: ${filePath}`)
        }
      })
    }
  }
}
